package model

import (
	"errors"
	cErr "go-build-admin/app/pkg/error"
	"go-build-admin/app/pkg/random"
	"go-build-admin/conf"
	"go-build-admin/utils"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

type Admin struct {
	ID            int32    `gorm:"column:id;primaryKey;autoIncrement:true;comment:ID" json:"id"`        // ID
	Username      string   `gorm:"column:username;not null;comment:用户名" json:"username"`                // 用户名
	Nickname      string   `gorm:"column:nickname;not null;comment:昵称" json:"nickname"`                 // 昵称
	Avatar        string   `gorm:"column:avatar;not null;comment:头像" json:"avatar"`                     // 头像
	Email         string   `gorm:"column:email;not null;comment:邮箱" json:"email"`                       // 邮箱
	Mobile        string   `gorm:"column:mobile;not null;comment:手机" json:"mobile"`                     // 手机
	LoginFailure  int32    `gorm:"column:login_failure;not null;comment:登录失败次数" json:"login_failure"`   // 登录失败次数
	LastLoginTime int64    `gorm:"column:last_login_time;comment:上次登录时间" json:"last_login_time"`        // 上次登录时间
	LastLoginIP   string   `gorm:"column:last_login_ip;not null;comment:上次登录IP" json:"last_login_ip"`   // 上次登录IP
	Password      string   `gorm:"column:password;not null;comment:密码" json:"password"`                 // 密码
	Salt          string   `gorm:"column:salt;not null;comment:密码盐" json:"salt"`                        // 密码盐
	Motto         string   `gorm:"column:motto;not null;comment:签名" json:"motto"`                       // 签名
	Status        string   `gorm:"column:status;not null;default:1;comment:状态:0=禁用,1=启用" json:"status"` // 状态:0=禁用,1=启用
	UpdateTime    int64    `gorm:"autoUpdateTime;column:update_time;comment:更新时间" json:"update_time"`   // 更新时间
	CreateTime    int64    `gorm:"autoCreateTime;column:create_time;comment:创建时间" json:"create_time"`   // 创建时间
	GroupArr      []int32  `gorm:"-" json:"group_arr"`
	GroupNameArr  []string `gorm:"-" json:"group_name_arr"`
}

type AdminModel struct {
	BaseModel
	config *conf.Configuration
}

func NewAdminModel(sqlDB *gorm.DB, config *conf.Configuration) *AdminModel {
	return &AdminModel{
		BaseModel: BaseModel{
			TableName:        config.Database.Prefix + "admin",
			Key:              "id",
			QuickSearchField: "username,nickname",
			DataLimit:        "",
			sqlDB:            sqlDB,
		},
		config: config,
	}
}

func (s *AdminModel) DealData(ctx *gin.Context, data *Admin) error {
	data.Avatar = utils.DefaultUrl(data.Avatar, s.config.App.DefaultAvatar)

	groups := []struct {
		Id   int32
		Name string
	}{}
	prefix := s.config.Database.Prefix
	err := s.sqlDB.Table(prefix+"admin_group_access").
		Joins("left join "+prefix+"admin_group g on g.id="+prefix+"admin_group_access.group_id").
		Select("g.id as id,g.name as name").
		Where(prefix+"admin_group_access.uid=?", data.ID).Scan(&groups).Error

	if err != nil {
		return err
	}
	for _, v := range groups {
		data.GroupArr = append(data.GroupArr, v.Id)
		data.GroupNameArr = append(data.GroupNameArr, v.Name)
	}
	return nil
}

func (s *AdminModel) GetOne(ctx *gin.Context, id int32) (Admin, error) {
	data := Admin{}
	if err := s.sqlDB.Omit("password", "salt", "login_failure").Where("id=?", id).Limit(1).First(&data).Error; err != nil {
		return data, err
	}
	if err := s.DealData(ctx, &data); err != nil {
		return data, err
	}
	return data, nil
}

func (s *AdminModel) GetGroupArr(ctx *gin.Context, id int32) (groupIds []int32, err error) {
	err = s.sqlDB.Model(&AdminGroupAccess{}).Where("uid=?", id).Pluck("group_id", &groupIds).Error
	return
}

func (s *AdminModel) GetGroupNameArr(ctx *gin.Context, id int32) (groupNames []string, err error) {
	prefix := s.config.Database.Prefix
	err = s.sqlDB.Model(&AdminGroupAccess{}).
		Joins("left join "+prefix+"admin_group on "+prefix+"admin_group_access.group_id = "+prefix+"admin_group.id").Where("uid=?", id).Pluck("name", &groupNames).Error
	return
}

func (s *AdminModel) List(ctx *gin.Context) (list []*Admin, total int64, err error) {
	whereS, whereP, orderS, limit, offset, err := QueryBuilder(ctx, s.TableInfo(), nil)
	if err != nil {
		return
	}
	db := s.sqlDB.Model(&Admin{}).Where(whereS, whereP...)
	if err = db.Count(&total).Error; err != nil {
		return
	}
	err = db.Omit("password", "salt", "login_failure").Order(orderS).Limit(limit).Offset(offset).Find(&list).Error
	for _, v := range list {
		if err = s.DealData(ctx, v); err != nil {
			return
		}
	}
	return
}

func (s *AdminModel) Add(ctx *gin.Context, admin Admin, groups []string) error {
	//判断是否有重名的账号
	if err := s.sqlDB.Where("username=?", admin.Username).Take(&Admin{}).Error; !errors.Is(err, gorm.ErrRecordNotFound) {
		return cErr.BadRequest("Account exist")
	}

	tx := s.sqlDB.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()

	if err := tx.Omit("login_failure", "last_login_time", "last_login_ip").Create(&admin).Error; err != nil {
		tx.Rollback()
		return err

	}

	access := []map[string]interface{}{}
	for _, v := range groups {
		access = append(access, map[string]interface{}{
			"uid": admin.ID, "group_id": v,
		})
	}

	if err := tx.Model(&AdminGroupAccess{}).Create(access).Error; err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit().Error
}

func (s *AdminModel) Edit(ctx *gin.Context, admin Admin, omit []string, groups []string) error {
	//判断是否有重名的账号
	if err := s.sqlDB.Where("id<>? and username=?", admin.ID, admin.Username).Take(&Admin{}).Error; !errors.Is(err, gorm.ErrRecordNotFound) {
		return cErr.BadRequest("Account exist")
	}

	tx := s.sqlDB.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()

	if err := tx.Omit(omit...).Save(&admin).Error; err != nil {
		tx.Rollback()
		return err

	}

	if len(groups) > 0 {
		if err := tx.Model(&AdminGroupAccess{}).Where("uid=?", admin.ID).Delete(nil).Error; err != nil {
			tx.Rollback()
			return err
		}
		access := []map[string]interface{}{}
		for _, v := range groups {
			access = append(access, map[string]interface{}{
				"uid": admin.ID, "group_id": v,
			})
		}

		if err := tx.Model(&AdminGroupAccess{}).Create(access).Error; err != nil {
			tx.Rollback()
			return err
		}
	}

	return tx.Commit().Error
}

func (s *AdminModel) SelfEdit(ctx *gin.Context, admin Admin, selectField []string) error {
	tx := s.sqlDB.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()

	if err := tx.Select(selectField).Save(&admin).Error; err != nil {
		tx.Rollback()
		return err

	}
	return tx.Commit().Error
}

func (s *AdminModel) ResetPassword(ctx *gin.Context, id int32, password string) error {
	salt := random.Build("alnum", 16)
	password = utils.EncryptPassword(password, salt)
	err := s.sqlDB.Model(&Admin{}).Where("id=?", id).Updates(map[string]interface{}{
		"salt":     salt,
		"password": password,
	}).Error
	return err
}

func (s *AdminModel) Del(ctx *gin.Context, ids interface{}) error {
	err := s.sqlDB.Model(&Admin{}).Scopes(LimitAdminIds(ctx)).Where(" id in ? ", ids).Delete(nil).Error
	return err
}
